//Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Collections.Generic;
using System.Text;
using Eas.WebCrawler.Model.Core;
using Eas.WebCrawler.Interfaces;

namespace Eas.WebCrawler.Model.Actions {
    internal class GetResponseAction : ProcessingAction{
        #region ProcessingAction Members

        public void PerformCompletingAction(WorkerContext context, BaseProcessingState state, IAsyncResult result) {

            context.EventReporter.ReportTrace(TraceLevel.Annoying, state, "Calling EndGetResponse as CompletingAction");
            context.StateTracker.CloseResult(result);
            try {
                state.WebResponse = context.WebGateway.EndGetResponse(state, result);
            }
            catch (System.Exception) { }
        }

        public IAsyncResult BeginAction(WorkerContext context, Eas.WebCrawler.Model.Core.BaseProcessingState state, AsyncCallback callback) {
            state.WebRequest = context.WebGateway.CreateWebRequest(state);
            context.EventReporter.ReportTrace(TraceLevel.Annoying, state, "Calling BeginGetResponse");

            IAsyncResult result = null;
            try {
                result =context.WebGateway.BeginGetResponse(state, callback, state);
            }
            catch (System.Net.WebException exception) {
                throw new WebFailureException("Failed to begin to get response from server: " + exception.Message, context.WebGateway.GetResponseCode(exception), exception);
            }
            catch (System.Exception exception) {
                throw new WebFailureException("Failed to begin to get response from server: " + exception.Message, 500, exception);
            }

            context.EventReporter.ReportTrace(TraceLevel.Annoying, state, "Returned from BeginGetResponse");
            context.StateTracker.OpenResult(result, this);
            return result;
        }

        public ProcessingAction EndAction(WorkerContext context, Eas.WebCrawler.Model.Core.BaseProcessingState state, IAsyncResult result) {
            context.EventReporter.ReportTrace(TraceLevel.Annoying, state, "Calling EndGetResponse");
            context.StateTracker.CloseResult(result);
            try {
                state.WebResponse = context.WebGateway.EndGetResponse(state, result);
            }
            catch (System.Net.WebException exception) {
                try { if (state.WebRequest != null)state.WebRequest.Abort(); }catch (System.Exception) { }
                throw new WebFailureException("Failed to get response from server: " + exception.Message, context.WebGateway.GetResponseCode(exception), exception);
            }
            catch (System.Exception exception) {
                try { if (state.WebRequest != null)state.WebRequest.Abort(); }catch (System.Exception) { }
                throw new WebFailureException("Failed to get response from server: " + exception.Message, 500, exception);
            }
            

            context.EventReporter.ReportTrace(TraceLevel.Annoying, state, "Checking for redirect.");
            if (context.WebGateway.IsRedirect(state)) {
                context.EventReporter.ReportTrace(TraceLevel.Annoying, state, "Response was redirect.");
                return PerformRedirect(state,context);
            }
            else {
                context.EventReporter.ReportTrace(TraceLevel.Annoying, state, "Response Was Not Redirect");
                context.EventReporter.ReportTrace(TraceLevel.Annoying, state, "Calling GetResponseStream.");

                try {
                    state.ResponseStream = context.WebGateway.GetResponseStream(state);
                }
                catch (System.Net.WebException exception) {
                    throw new WebFailureException("Failed to get response stream from server: " + exception.Message, context.WebGateway.GetResponseCode(exception), exception);
                }
                catch (System.Exception exception) {
                    throw new WebFailureException("Failed to get response stream from server: " + exception.Message, 500, exception);
                }

                context.EventReporter.ReportTrace(TraceLevel.Annoying, state, "Initializing Storage and StorageWriter.");

                InitializeStateFromWebResponse(state, context);

                ProcessingAction nextAction = context.ActionFactory.CreateStreamReadAction(state, context);
                return nextAction;
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
        private static void InitializeStateFromWebResponse(BaseProcessingState state, WorkerContext context) {
            state.ResponseStore = context.WebCrawlerContext.StorageFactory.Create(state);
            context.EventReporter.ReportTrace(TraceLevel.Annoying, state, "Setting TemporaryStorage to {0}.", state.ResponseStore);

            state.IsTextResponse = context.WebGateway.ResponseIsText(state);
            context.EventReporter.ReportTrace(TraceLevel.Annoying, state, "Setting IsTextResponse to {0}.", state.IsTextResponse);

            if (state.IsTextResponse) {
                state.StorageWriter = new Eas.WebCrawler.Model.StorageWriters.TextStorageWriter();
            }
            else {
                state.StorageWriter = new Eas.WebCrawler.Model.StorageWriters.BinaryStorageWriter();
            }
            state.StorageWriter.Initialize(state.ResponseStore);
        }

        public Eas.WebCrawler.Interfaces.WorkerState WorkerState {
            get { return Eas.WebCrawler.Interfaces.WorkerState.Working; }
        }

        public ActionId ActionId {
            get { return ActionId.GetResponse; }
        }

        #endregion

        private ProcessingAction PerformRedirect(BaseProcessingState state, WorkerContext context) {

            int currentDepth = state.RedirectDepth;
            context.EventReporter.ReportTrace(TraceLevel.Annoying, state, "Redirect Depth is {0}", state.RedirectDepth);
            
            if (state.RedirectDepth > 10) {
                throw new WebFailureException("Maximum redirect depth reached.", 599, null);
            }
            Request request = new Request(context.WebGateway.GetRedirectUri(state), state.Request);
            try {
                state.WebResponse.Close();
            }
            catch (System.Exception) { }
            try {
                state.WebRequest.Abort();
            }
            catch (System.Exception) { }
            state.WebRequest = null;
            state.WebResponse = null;
            state.Request = request;
            state.RedirectDepth = currentDepth + 1;
            return this;
        }

    }
}
